package pATT.bNEdit.inference;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import pATT.bNEdit.base.Nodo;
import pATT.bNEdit.base.RedBayes;


public abstract class AlgoritmoInferencia {

  /**
   * Devuelve un vector con los valores del potencial correspondiente a la
   * consulta de la variable dada sobre la red de bayes
   *
   * @param rb red de bayes sobre la que se realiza la consulta
   * @param variable nombre de la variable a consultar
   * @return Vector
   */
  public abstract Vector query(RedBayes rb, String variable);

  /**
   * Devuelve un valor correspondiente al potencial resultante de la consulta
   * de la variable dada sobre la red de bayes y un estado en particular
   *
   * @param rb red de bayes sobre la que se realiza la consulta
   * @param variable nombre de la variable a consultar
   * @param estado String
   * @return double
   */
  public abstract double query(RedBayes rb, String variable, String estado);

  /**
   * Uses Bayes' chain rule to calculate any given configuration's likelyhood.
   * Any inference algorithm that optimizes this kind of queries should override
   * this method.
   *
   * @param bayesianNetwork RedBayes - the bayesian network from which to infer
   * @param configuration Map - the varName->value mapping of the configuration
   * @return double - the configuration's likelyhood on the net
   */
  @SuppressWarnings("unchecked")
public double query(RedBayes bayesianNetwork, Map configuration) {
    double p = 1;
    Vector fakeObservations = new Vector();
    boolean contradiction = false;
    // add as evidence all the values inside the configuration that
    // haven't already been observed
    for (Iterator varNamesIterator = configuration.keySet().iterator();
         varNamesIterator.hasNext() && !contradiction; ) {
      String varName = (String)varNamesIterator.next();
      String value = (String)configuration.get(varName);
      Nodo var = bayesianNetwork.getNodo(varName);
      if (!var.estaObservada()) {
        var.setObservacion(value);
        fakeObservations.add(varName);
      }
      else if (!var.estadoObservado().equals(value)){
        contradiction = true;
      }
    }
    // as the "fake evidence" is removed, multiply the current value of p by
    // the likelyhood of the removed piece of evidence's value given the rest
    // of it
    Enumeration fakeObservationsEnum = fakeObservations.elements();
    while (fakeObservationsEnum.hasMoreElements()) {
      String varName = (String)fakeObservationsEnum.nextElement();
      String value = (String)configuration.get(varName);
      Nodo var = bayesianNetwork.getNodo(varName);
      var.delObservacion();
      if (!contradiction)
        p *= query(bayesianNetwork, varName, value);
    }
    if (contradiction)
      return 0;
    else
      return p;
  }

}
